/**
 * @Author       : Hejh(3010733382@qq.com)
 * @Version      : V1.0
 * @Date         : 2024-01-26 18:32:44
 * @Description  : 分享接口验证中间件
 */

const Joi = require('joi')
const { Op } = require('sequelize')
const R = require('@/models/Response')
const { Share, Source } = require('@/models/model')
const {
  ERROR_SHARE_CODE,
  ERROR_SYSTEM_CODE,
  ERROR_SOURCE_CODE,
  ERROR_USER_CODE
} = require('@/constants/errorCode')
const { IS_INTEGER, IS_UNSAFE_INTEGER } = require('@/constants/joiSchema')
const { dumpCheck: globalDumpCheck } = require('@/utils/dumpCheck')
const { getUserPermissionsById } = require('@/controller/userController')
const {
  commonVerifySourceAuth
} = require('../authMiddlewares/verifySourceAuth')

const shareSchema = {
  shareIds: Joi.array().items(IS_UNSAFE_INTEGER),
  shareId: IS_UNSAFE_INTEGER,
  userId: IS_UNSAFE_INTEGER,
  sourceId: IS_UNSAFE_INTEGER,
  page: IS_INTEGER.min(1).default(1),
  pageSize: IS_INTEGER.min(1).default(999),
  title: Joi.string().min(1).max(255),
  code: Joi.string().min(0).max(20),
  isLink: IS_INTEGER.valid(0, 1),
  isShareTo: IS_INTEGER.valid(0, 1),
  status: IS_INTEGER.valid(0, 1),
  type: IS_INTEGER.valid(0, 1),
  deadline: IS_UNSAFE_INTEGER,
  startTime: Joi.date(),
  endTime: Joi.date().when('startTime', {
    is: Joi.exist(),
    then: Joi.date().greater(Joi.ref('startTime')),
    otherwise: Joi.date().allow(null)
  }),
  options: Joi.object(),
  authTo: Joi.array().items(
    Joi.object({
      targetType: IS_INTEGER.valid(1, 2).required(),
      targetId: IS_UNSAFE_INTEGER.required(),
      roleId: IS_UNSAFE_INTEGER.required()
    })
  )
}

const dumpCheck = {
  /**
   * @author: Hejh(3010733382@qq.com)
   * @description: 验证分享ID的合法性
   * @param {array<string>} shareIds 分享ID数组
   * @param {boolean} isLink 需要为外链
   * @param {boolean} isShareTo 需要为内部分享
   */
  checkShareIds: async (shareIds, isLink, isShareTo) => {
    let newshareIds = [...new Set(shareIds)]
    // 不能出现重复的
    if (newshareIds.length !== shareIds.length) {
      return false
    }

    newshareIds = newshareIds.map((item) => BigInt(item))

    if (newshareIds.length === 0) {
      return true
    }

    // 查看不重复的和数据库的是否对的上
    const myWhere = {
      id: {
        [Op.in]: newshareIds
      }
    }
    if (isLink) {
      myWhere.isLink = 1
    }
    if (isShareTo) {
      myWhere.isShareTo = 1
    }
    const shares = await Share.findAll({
      where: myWhere
    })
    if (shares.length !== newshareIds.length) {
      return false
    }

    return true
  }
}

/**
 * @description 分享列表查询的参数验证
 * @param {*} ctx
 * @param {*} next
 * @returns
 */
module.exports.listShareValidate = async (ctx, next) => {
  const schema = Joi.object({
    page: shareSchema.page,
    pageSize: shareSchema.pageSize,
    userId: shareSchema.userId,
    type: shareSchema.type,
    startTime: shareSchema.startTime,
    endTime: shareSchema.endTime
  }).validate(ctx.query)

  if (schema.error) {
    ctx.body = R.error(ERROR_SYSTEM_CODE.PARAMS_SCHEMA_ERROR)
    return
  }
  ctx.newQuery = schema.value
  await next()
}

/**
 * @description 分享列表查询的参数验证
 * @param {*} ctx
 * @param {*} next
 * @returns
 */
module.exports.listUserShareValidate = async (ctx, next) => {
  const schema = Joi.object({
    page: shareSchema.page,
    pageSize: shareSchema.pageSize,
    type: shareSchema.type,
    filename: Joi.string().allow('')
  }).validate(ctx.query)

  if (schema.error) {
    ctx.body = R.error(ERROR_SYSTEM_CODE.PARAMS_SCHEMA_ERROR)
    return
  }
  ctx.newQuery = schema.value
  await next()
}

/**
 * @description 创建分享时的参数验证
 * @param {*} ctx
 * @param {*} next
 * @returns
 */
module.exports.createShareValidate = async (ctx, next) => {
  const schema = Joi.object({
    title: shareSchema.title.required(),
    sourceId: shareSchema.sourceId.required()
  }).validate(ctx.request.body)

  if (schema.error) {
    ctx.body = R.error(ERROR_SYSTEM_CODE.PARAMS_SCHEMA_ERROR)
    return
  }

  const { sourceId } = ctx.request.body
  const b = await globalDumpCheck.checkSourceIds(
    [sourceId],
    [{ field: 'deleteFlag', value: 0 }]
  )

  // 验证资源ID的合法性
  if (!b) {
    ctx.body = R.error(ERROR_SOURCE_CODE.SOURCE_ID_ERROR)
    return
  }

  // 同一个资源只能被共享一次
  const one = await Share.findOne({
    where: {
      sourceId: BigInt(sourceId)
    }
  })

  if (one) {
    // 说明资源已经被共享
    ctx.body = R.success()
    return
  }

  // TODO :需要验证用户有分享的权限
  // 验证用户对该资源的权限，个人文件---需要创建者为自己
  // 部门文件---需要创建者对其拥有分享权限
  const source = await Source.findByPk(BigInt(sourceId))
  if (+source.targetType === 1 && source.createUser !== ctx.user.id) {
    ctx.body = R.error(ERROR_SYSTEM_CODE.PERMISSION_ERROR)
    return
  }

  if (+source.targetType === 2) {
    // 对于部门文件，需要用户对该文件拥有权限
    const res = await commonVerifySourceAuth(ctx.user.id, source.id, [
      'doc.auth.share'
    ])

    if (!res) {
      ctx.body = R.error(ERROR_SYSTEM_CODE.PERMISSION_ERROR)
      return
    }
  }
  // 到这里说明参数没有问题
  await next()
}

/**
 * @author: Hejh(3010733382@qq.com)
 * @description: 更新分享时的参数验证
 * @param {*} ctx
 * @param {*} next
 */
module.exports.updateShareValidate = async (ctx, next) => {
  const schema = Joi.object({
    shareId: shareSchema.shareId.required(),
    isLink: shareSchema.isLink,
    isShareTo: shareSchema.isShareTo,
    title: shareSchema.title.when('isLink', { is: 1, then: Joi.required() }),
    code: shareSchema.code.when('isLink', { is: 1, then: Joi.required() }),
    deadline: shareSchema.deadline,
    options: shareSchema.options,
    isActive: shareSchema.status,
    authTo: shareSchema.authTo.when('isShareTo', {
      is: 1,
      then: Joi.required()
    })
  })
    .oxor('isLink', 'isShareTo')
    .validate(ctx.request.body)

  if (schema.error) {
    ctx.body = R.error(ERROR_SYSTEM_CODE.PARAMS_SCHEMA_ERROR)
    return
  }

  // 根据外链分享还是内部分享进行参数验证
  const { shareId, isShareTo, authTo, isLink } = ctx.request.body

  // 系统权限： 首先确定用户是否有更新的权限，isLink：外链权限，isShareTo：内部协作
  const user = await getUserPermissionsById(ctx.user.id)
  if (isLink) {
    const isPass = user.role.Permissions.some((item) => {
      return ['source.shareLink'].includes(item.ename)
    })

    if (!isPass) {
      ctx.body = R.error(ERROR_SYSTEM_CODE.PERMISSION_ERROR)
      return
    }
  }

  if (isShareTo) {
    const isPass = user.role.Permissions.some((item) => {
      return ['source.share'].includes(item.ename)
    })

    if (!isPass) {
      ctx.body = R.error(ERROR_SYSTEM_CODE.PERMISSION_ERROR)
      return
    }
  }

  // 验证shareId的合法性
  const b1 = await dumpCheck.checkShareIds([shareId], false, false)
  if (!b1) {
    ctx.body = R.error(ERROR_SHARE_CODE.SHARE_ID_ERROR)
    return
  }

  if (isShareTo) {
    // 验证authTo字段数据的合法性，roleId需要为文档权限ID，targetId需要为部门或者用户ID，targetID不可重复
    // 而roleID是可以重复的
    const b3 = await globalDumpCheck.checkAuthTo(ctx, authTo)
    if (!b3) {
      return
    }
  }

  // 对于更新的shareId，其对应的sourceId如果为个人文件，需要为自己
  // 如果是部门文件，需要对该部门文件有分享权限
  const shareInfo = await Share.findByPk(BigInt(shareId), {
    include: [
      {
        model: Source,
        as: 'source'
      }
    ]
  })

  if (
    +shareInfo.source.targetType === 1 &&
    shareInfo.source.createUser !== ctx.user.id
  ) {
    // 说明没有权限
    ctx.body = R.error(ERROR_SYSTEM_CODE.PERMISSION_ERROR)
    return
  }

  if (+shareInfo.source.targetType === 2) {
    // 验证文档权限
    let auth = ['doc.auth.share']
    // if (isLink) {
    //   auth = ['source.shareLink']
    // }
    const res = await commonVerifySourceAuth(
      ctx.user.id,
      shareInfo.source.id,
      auth
    )
    if (!res) {
      ctx.body = R.error(ERROR_SYSTEM_CODE.PERMISSION_ERROR)
      return
    }
  }
  await next()
}

/**
 * @author: Hejh(3010733382@qq.com)
 * @description: 取消分享的参数验证
 * @param {*} ctx
 * @param {*} next
 */
module.exports.cancelShareValidate = async (ctx, next) => {
  const schema = Joi.object({
    shareIds: shareSchema.shareIds.required(),
    type: Joi.valid(0, 1) // 0-外链，1-协作
  }).validate(ctx.request.body)

  if (schema.error) {
    ctx.body = R.error(ERROR_SYSTEM_CODE.PARAMS_SCHEMA_ERROR)
    return
  }

  // 根据外链分享还是内部分享进行参数验证
  const { shareIds, type } = ctx.request.body

  // 系统权限：首先确定用户是否有取消的权限，0：外链权限，1：内部协作
  const user = await getUserPermissionsById(ctx.user.id)
  if (+type === 0) {
    const isPass = user.role.Permissions.some((item) => {
      return ['source.shareLink'].includes(item.ename)
    })

    if (!isPass) {
      ctx.body = R.error(ERROR_SYSTEM_CODE.PERMISSION_ERROR)
      return
    }
  }

  if (+type === 1) {
    const isPass = user.role.Permissions.some((item) => {
      return ['source.share'].includes(item.ename)
    })

    if (!isPass) {
      ctx.body = R.error(ERROR_SYSTEM_CODE.PERMISSION_ERROR)
      return
    }
  }

  // 验证shareId的合法性
  const b1 = await dumpCheck.checkShareIds(shareIds, false, false)
  if (!b1) {
    ctx.body = R.error(ERROR_SHARE_CODE.SHARE_ID_ERROR)
    return
  }

  // 对于更新的shareId，其对应的sourceId如果为个人文件，需要为自己
  // 如果是部门文件，需要对该部门文件有分享权限
  for (let i = 0; i < shareIds.length; i++) {
    const shareInfo = await Share.findByPk(BigInt(shareIds[i]), {
      include: [
        {
          model: Source,
          as: 'source'
        }
      ]
    })

    if (
      +shareInfo.source.targetType === 1 &&
      shareInfo.source.createUser !== ctx.user.id
    ) {
      // 说明没有权限
      ctx.body = R.error(ERROR_SYSTEM_CODE.PERMISSION_ERROR)
      return
    }

    if (+shareInfo.source.targetType === 2) {
      let auth = ['doc.auth.share']
      // if (+type === 0) {
      //   auth = ['source.shareLink']
      // }
      // 对于部门文件，需要用户对该文件拥有权限
      const res = await commonVerifySourceAuth(
        ctx.user.id,
        shareInfo.source.id,
        auth
      )

      if (!res) {
        ctx.body = R.error(ERROR_SYSTEM_CODE.PERMISSION_ERROR)
        return
      }
    }
  }

  await next()
}

/**
 * @author: Hejh(3010733382@qq.com)
 * @description: 开启/关闭分享接口验证
 * @param {*} ctx
 * @param {*} next
 */
module.exports.setStatusValidate = async (ctx, next) => {
  const schema = Joi.object({
    shareIds: shareSchema.shareIds.required(),
    status: shareSchema.status.required()
  }).validate(ctx.request.body)

  if (schema.error) {
    ctx.body = R.error(ERROR_SYSTEM_CODE.PARAMS_SCHEMA_ERROR)
    return
  }

  const { shareIds } = ctx.request.body

  // 验证分享ID数组的合法性
  const b4 = await dumpCheck.checkShareIds(shareIds)
  if (!b4) {
    ctx.body = R.error(ERROR_SHARE_CODE.SHARE_ID_ERROR)
    return
  }
  await next()
}

/**
 * @author: Hejh(3010733382@qq.com)
 * @description: 批量删除分享接口的参数验证
 * @param {*} ctx
 * @param {*} next
 */
module.exports.delSharesValidate = async (ctx, next) => {
  const schema = Joi.object({
    shareIds: shareSchema.shareIds.required()
  }).validate(ctx.request.body)

  if (schema.error) {
    ctx.body = R.error(ERROR_SYSTEM_CODE.PARAMS_SCHEMA_ERROR)
    return
  }

  const { shareIds } = ctx.request.body

  // 验证分享ID数组的合法性
  const b4 = await dumpCheck.checkShareIds(shareIds)
  if (!b4) {
    ctx.body = R.error(ERROR_SHARE_CODE.SHARE_ID_ERROR)
    return
  }

  // 这些ID可能部分isShareTo为1，需要级联删除，需要判断全为1或者0码？
  // 推荐直接删除，因为查询也需要耗时
  await next()
}

/**
 * @author: Hejh(3010733382@qq.com)
 * @description: 分享浏览数添加接口
 * @param {*} ctx
 * @param {*} next
 */
module.exports.shareNumViewAddValidate = async (ctx, next) => {
  const schema = Joi.object({
    shareId: shareSchema.shareId.required()
  }).validate(ctx.request.body)

  if (schema.error) {
    ctx.body = R.error(ERROR_SYSTEM_CODE.PARAMS_SCHEMA_ERROR)
    return
  }

  const { shareId } = ctx.request.body

  // 验证分享ID数组的合法性
  const b4 = await dumpCheck.checkShareIds([shareId], true)
  if (!b4) {
    ctx.body = R.error(ERROR_SHARE_CODE.SHARE_ID_ERROR)
    return
  }
  await next()
}

/**
 * @description 分享列表查询的参数验证
 * @param {*} ctx
 * @param {*} next
 * @returns
 */
module.exports.listUserInnerShareValidate = async (ctx, next) => {
  const schema = Joi.object({
    page: shareSchema.page,
    pageSize: shareSchema.pageSize,
    filename: Joi.string().allow('')
  }).validate(ctx.query)

  if (schema.error) {
    ctx.body = R.error(ERROR_SYSTEM_CODE.PARAMS_SCHEMA_ERROR)
    return
  }
  ctx.query = schema.value
  await next()
}
